const std = @import("std");
const string = []const u8;
const gpa = std.heap.c_allocator;
const inquirer = @import("inquirer");
const detectlicense = @import("detect-license");
const knownfolders = @import("known-folders");
const ini = @import("ini");
const time = @import("time");
const extras = @import("extras");

const u = @import("./../util/funcs.zig");

//
//

pub fn execute(self_name: []const u8, args: [][:0]u8) !void {
    _ = self_name;

    std.debug.print("This utility will walk you through creating a zigmod.yml file.\n", .{});
    std.debug.print("That will give a good launching off point to get your next project started.\n", .{});
    std.debug.print("Press ^C at any time to quit.\n", .{});
    std.debug.print("\n", .{});

    const stdout = std.io.getStdOut().writer();
    const stdin = std.io.getStdIn().reader();
    const cwd = std.fs.cwd();

    const id = try inquirer.answer(stdout, "ID (this gets autogenerated):", string, "{s}", &u.random_string(48));

    const ptype = try inquirer.forEnum(stdout, stdin, "Are you making an application or a library?", gpa, enum { exe, lib }, null);

    const name = try inquirer.forString(stdout, stdin, "package name:", gpa, u.detect_pkgname(gpa, u.try_index(string, args, 0, ""), "") catch |err| switch (err) {
        error.NoBuildZig => {
            u.fail("init requires a build.zig file", .{});
        },
        else => |ee| return ee,
    });

    const entry = if (ptype == .lib) try inquirer.forString(stdout, stdin, "package entry point:", gpa, u.detct_mainfile(gpa, u.try_index(string, args, 1, ""), null, name) catch |err| switch (err) {
        error.CantFindMain => null,
        else => |ee| return ee,
    }) else null;

    const maybe_license_text = try detectlicense.detectInDir(gpa, cwd);
    const license = try inquirer.forString(stdout, stdin, "license:", gpa, maybe_license_text);

    const description = try inquirer.forString(stdout, stdin, "description:", gpa, null);

    std.debug.print("\n", .{});
    std.debug.print("About to write local zigmod.yml:\n", .{});

    std.debug.print("\n", .{});
    switch (ptype) {
        .exe => try writeExeManifest(stdout, id, name, license, description),
        .lib => try writeLibManifest(stdout, id, name, entry.?, license, description),
    }

    std.debug.print("\n", .{});
    switch (try inquirer.forConfirm(stdout, stdin, "Is this okay?", gpa)) {
        false => {
            std.debug.print("okay. quitting...", .{});
            return;
        },
        true => {
            const file = try cwd.createFile("zigmod.yml", .{});
            defer file.close();
            const w = file.writer();
            switch (ptype) {
                .exe => try writeExeManifest(w, id, name, license, description),
                .lib => try writeLibManifest(w, id, name, entry.?, license, description),
            }
            std.debug.print("\n", .{});
            std.debug.print("Successfully initialized new package {s}!\n", .{name});
        },
    }

    // ask about LICENSE
    if (maybe_license_text == null) {
        if (detectlicense.licenses.find(license)) |text| {
            if (try inquirer.forConfirm(stdout, stdin, "It appears you don't have a LICENSE file defined, would you like init to add it for you?", gpa)) {
                var realtext = text;
                realtext = try std.mem.replaceOwned(u8, gpa, realtext, "<year>", try inquirer.answer(
                    stdout,
                    "year:",
                    string,
                    "{s}",
                    try std.fmt.allocPrint(gpa, "{d}", .{time.DateTime.now().years}),
                ));
                realtext = try std.mem.replaceOwned(u8, gpa, realtext, "<copyright holders>", try inquirer.forString(
                    stdout,
                    stdin,
                    "copyright holder's name:",
                    gpa,
                    try guessCopyrightName(),
                ));

                const file = try cwd.createFile("LICENSE", .{});
                defer file.close();
                const w = file.writer();

                // properly format license text to fit within 80 columns
                var start: usize = 0;
                var end: usize = 0;
                var run: u32 = 0;
                var i: usize = 0;
                while (i < realtext.len) {
                    const c = realtext[i];
                    end += 1;
                    i += 1;

                    if (c == '\n') {
                        try w.writeAll(realtext[start..end]);
                        start = end;
                        run = 0;
                        i = start;
                        continue;
                    }
                    run += 1;

                    if (run >= 79) {
                        const s_ind = (std.mem.lastIndexOfScalar(u8, realtext[start..end], ' ') orelse end) + start;
                        const n_ind = (std.mem.lastIndexOfScalar(u8, realtext[start .. end - 1], '\n') orelse end) + start;
                        const ind = @min(s_ind, n_ind);
                        try w.print("{s}\n", .{realtext[start..ind]});
                        end = ind + 1;
                        start = end;
                        run = 0;
                        i = start;
                    }
                }
                try w.writeAll(realtext[start..realtext.len]);
            }
        }
    }

    // ask about .gitignore
    if (try extras.doesFolderExist(null, ".git")) {
        const do = try inquirer.forConfirm(stdout, stdin, "It appears you're using git. Do you want init to add Zigmod to your .gitignore?", gpa);
        if (do) {
            const exists = try extras.doesFileExist(null, ".gitignore");
            const file: std.fs.File = try (if (exists) cwd.openFile(".gitignore", .{ .mode = .read_write }) else cwd.createFile(".gitignore", .{}));
            defer file.close();
            const len = try file.getEndPos();
            if (len > 0) try file.seekTo(len - 1);
            const w = file.writer();
            if (len > 0 and (try file.reader().readByte()) != '\n') {
                try w.writeAll("\n");
            }
            if (!exists) try w.writeAll(".zig-cache\n");
            if (!exists) try w.writeAll("zig-out\n");
            try w.writeAll(".zigmod\n");
            try w.writeAll("deps.zig\n");
            try w.writeAll("files.zig\n");
            if (ptype == .lib) try w.writeAll("zigmod.lock\n");
        }
    }

    // ask about .gitattributes
    if (try extras.doesFolderExist(null, ".git")) {
        const do = try inquirer.forConfirm(stdout, stdin, "It appears you're using git. Do you want init to add Zigmod to your .gitattributes?", gpa);
        if (do) {
            const exists = try extras.doesFileExist(null, ".gitattributes");
            const file: std.fs.File = try (if (exists) cwd.openFile(".gitattributes", .{ .mode = .read_write }) else cwd.createFile(".gitattributes", .{}));
            defer file.close();
            const len = try file.getEndPos();
            if (len > 0) try file.seekTo(len - 1);
            const w = file.writer();
            if (len > 0 and (try file.reader().readByte()) != '\n') {
                try w.writeAll("\n");
            }
            if (!exists) try w.writeAll("* text=auto\n");
            if (!exists) try w.writeAll("*.zig text eol=lf\n");
            try w.writeAll("zigmod.* text eol=lf\n");
        }
    }
}

pub fn writeExeManifest(w: std.fs.File.Writer, id: string, name: string, license: ?string, description: ?string) !void {
    try w.print("id: {s}\n", .{id});
    try w.print("name: {s}\n", .{name});
    if (license) |_| try w.print("license: {s}\n", .{license.?});
    if (description) |_| try w.print("description: {s}\n", .{description.?});
    try w.writeAll("root_dependencies:\n");
}

pub fn writeLibManifest(w: std.fs.File.Writer, id: string, name: string, entry: string, license: string, description: string) !void {
    try w.print("id: {s}\n", .{id});
    try w.print("name: {s}\n", .{name});
    try w.print("main: {s}\n", .{entry});
    try w.print("license: {s}\n", .{license});
    try w.print("description: {s}\n", .{description});
    try w.writeAll("dependencies:\n");
}

fn guessCopyrightName() !?string {
    const home = (try knownfolders.open(gpa, .home, .{})).?;
    if (!(try extras.doesFileExist(home, ".gitconfig"))) return null;
    const file = try home.openFile(".gitconfig", .{});
    const content = try file.reader().readAllAlloc(gpa, 1024 * 1024);
    var iniO = try ini.parseIntoMap(content, gpa);
    return iniO.map.get("user.name");
}
